
/*
 *  Off-the-Record Messaging plugin for pidgin
 *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
 *                           <otr@cypherpunks.ca>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of version 2 of the GNU General Public License as
 *  published by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/* config.h */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

/* system headers */
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/stat.h>

/* libgcrypt headers */
#include <gcrypt.h>

/* md5_crypt headers */
#include "md5_crypt.h"

/* purple headers */
#include "pidgin.h"
#include "notify.h"
#include "version.h"
#include "util.h"
#include "debug.h"
#include "core.h"

#ifdef USING_GTK

/* purple GTK headers */
#include "gtkplugin.h"
#endif

/* libotr headers */
#include <libotr/privkey.h>
#include <libotr/proto.h>
#include <libotr/message.h>
#include <libotr/userstate.h>

/* purple-otr headers */
#include "ui.h"
#include "dialogs.h"
#include "otr-plugin.h"

#ifdef USING_GTK

/* purple-otr GTK headers */
#include "gtk-ui.h"
#include "gtk-dialog.h"
#endif

/* If we're using glib on Windows, we need to use g_fopen to open files.
 * On other platforms, it's also safe to use it.  If we're not using
 * glib, just use fopen. */
#ifdef USING_GTK

/* If we're cross-compiling, this might be wrong, so fix it. */
#ifdef WIN32
#undef G_OS_UNIX
#define G_OS_WIN32
#endif
#include <glib/gstdio.h>
#else
#define g_fopen fopen
#endif



PurplePlugin *otrg_plugin_handle;

/* We'll only use the one OtrlUserState. */
OtrlUserState otrg_plugin_userstate = NULL;

GList *MessageMD5List = NULL;

//gchar *test_var = NULL; 

const char *
get_message_type_name (const MessageType type)
{
  const gchar *typeName = NULL;

  switch (type) {
    case MD5CODE:
      typeName = g_strdup_printf ("%s", "MD5_MESSAGE");
      break;
    case MESSAGE:
      typeName = g_strdup_printf ("%s", "MESSAGE");
      break;
    case OTHER:
      typeName = g_strdup_printf ("%s", "OTHER");
      break;
    default:
      typeName = g_strdup_printf ("%s", "ERROR");
      break;
  }

  return typeName;

}
static MessageTaged *
parse_message (const char *message)
{
  MessageTaged *messageArr = NULL;
  MessageMD5 *messageMD5 = NULL;

  messageArr = g_new0 (MessageTaged, 1);
  messageMD5 = g_new0 (MessageMD5, 1);

  gchar *message_clone = g_strdup (message);

  gchar *start_tag = NULL;

  start_tag = g_strrstr (message_clone, "?RECV?");

//check if this is a real message or a md5 info.
  if (start_tag) {
  //this is a normal otred message,
  //try to parse it and get the receiver and sender as well as the uid
    *start_tag = '\0';
    messageArr->type = MESSAGE;

    gchar *real_receiver = NULL;

    real_receiver = start_tag + 6;

    gchar *tag_recv_end = NULL;

    tag_recv_end = g_strrstr (real_receiver, "?ENDRECV?\t");

  //need a error handle, but normally, it's impossible not find the receiver.
    *tag_recv_end = '\0';

    gchar *real_sender = NULL;

    real_sender = tag_recv_end + 16;

    gchar *tag_send_end = NULL;

    tag_send_end = g_strrstr (real_sender, "?ENDSEND?\t");

    *tag_send_end = '\0';

    gchar *uid = NULL;

    uid = tag_send_end + 16;

    gchar *tag_uid_end = NULL;

    tag_uid_end = g_strrstr (uid, "?ENDU_ID?\t");

    *tag_uid_end = '\0';

    message_clone = g_strconcat (message_clone, (tag_uid_end + 10), NULL);
  //sometime the Pidgin will add some html code before my tags, so I need to recover those html code after I am done with mine.
    gchar *message_md5 = NULL;

    message_md5 = md5_encode (message_clone);

    glong uniqueID = (glong) uid;

    messageArr->rsender = g_strdup (real_sender);
    messageArr->rreceiver = g_strdup (real_receiver);
    messageArr->uniqueID = uniqueID;
    messageArr->message = g_strdup (message_clone);
    messageArr->md5sum = g_strdup (message_md5);

    messageMD5->rsender = g_strdup (real_sender);
    messageMD5->rreceiver = g_strdup (real_receiver);
    messageMD5->uniqueID = uniqueID;
    messageMD5->md5sum = g_strdup (message_md5);
    messageMD5->is_verified = FALSE;

    MessageMD5List = g_list_append (MessageMD5List, messageMD5);

  //g_free(real_sender);
  //g_free(real_receiver);
    g_free (message_clone);
  //g_free(uid);
    g_free (message_md5);

    return messageArr;

  } else {
    start_tag = strstr (message_clone, "?MD5?");

    if (start_tag) {
      *start_tag = '\0';

      gchar *md5sum = NULL;

      md5sum = start_tag + 9;

      gchar *tag_md5_end = NULL;

      tag_md5_end = g_strrstr (md5sum, "?ENDMD5?\t");

    //need a error handle, but normally, it's impossible not find the receiver.
      *tag_md5_end = '\0';

      gchar *uid = NULL;

      uid = tag_md5_end + 15;

      gchar *tag_uid_end = NULL;

      tag_uid_end = g_strrstr (uid, "?ENDU_ID?");

      *tag_uid_end = '\0';

      glong uniqueID = (glong) uid;

      messageArr->type = MD5CODE;
      messageArr->md5sum = g_strdup (md5sum);
      messageArr->uniqueID = uniqueID;

      g_free (message_clone);

      return messageArr;

    } else {
      messageArr->type = OTHER;
      messageArr->message = g_strdup (message_clone);

      g_free (message_clone);
      return messageArr;
    }
  }
}

/* Change a message to be as a verified, and change the row color to green */
static void
md5_verified (const MessageMD5 * messageMD5)
{
  GList *node;
  MessageMD5 *this_messageMD5 = NULL;

  if (g_list_length (MessageMD5List) > 0) {
    for (node = MessageMD5List; node != NULL; node = node->next) {
      this_messageMD5 = (MessageMD5 *) node->data;
      if (!strcmp (this_messageMD5->rreceiver, messageMD5->rreceiver)
          && !strcmp (this_messageMD5->md5sum, messageMD5->md5sum)
          && !strcmp (this_messageMD5->rsender, messageMD5->rsender)
          && (this_messageMD5->uniqueID == messageMD5->uniqueID)) {

        this_messageMD5->is_verified = TRUE;
      }
    }
  }


  if (md5_dialog) {
  //otrg_gtk_dialog_update_md5list_ui();


  /*
   * destory the dialog if exist and recreate it... 
   */

    gtk_widget_destroy (md5_dialog);
    md5_dialog = NULL;
    otrg_gtk_dialog_make_md5list_ui ();

  }
}

int
find_replace_account_chat_info (const char *accountname, const char *hostname,
                                const PurpleConvChat * chat,
                                TrustLevel conversation_stat)
{
  PurpleConversation *conv = purple_conv_chat_get_conversation (chat);

  PurpleAccount *account = purple_conversation_get_account (conv);

  const gchar *protocol_name = purple_account_get_protocol_name (account);

  const gchar *tmp;
  gchar *data;
  xmlnode *node, *child, *child_child;

//const gchar *chatinfofile = g_strdup_printf("%s.xml", CHATINFOFILENAME);

  if ((node =
       purple_util_read_xml_from_file ("otr.chatinfo.xml",
                                       "chat room info")) == NULL) {
    node = xmlnode_new ("chatroominfos");
    child = xmlnode_new_child (node, "chatinfo");
    child_child = xmlnode_new_child (child, "account");
    xmlnode_set_attrib (child_child, "protocol", protocol_name);
    xmlnode_insert_data (child_child, accountname, -1);

    tmp = g_strdup_printf ("%d", purple_conv_chat_get_id (chat));
    child_child = xmlnode_new_child (child, "chatroom_id");
    xmlnode_insert_data (child_child, tmp, -1);

    child_child = xmlnode_new_child (child, "virtual_server");
    xmlnode_insert_data (child_child, hostname, -1);

    tmp = g_strdup_printf ("%d", conversation_stat);
    child_child = xmlnode_new_child (child, "security_stat");
    xmlnode_insert_data (child_child, tmp, -1);

    data = xmlnode_to_formatted_str (node, NULL);
    purple_util_write_data_to_file ("otr.chatinfo.xml", data, -1);
    g_free (data);
    xmlnode_free (node);
  } else {
    xmlnode *new_node = NULL;

    new_node = xmlnode_new ("chatroominfos");

    child = xmlnode_new_child (new_node, "chatinfo");
    child_child = xmlnode_new_child (child, "account");
    xmlnode_set_attrib (child_child, "protocol", protocol_name);
    xmlnode_insert_data (child_child, accountname, -1);

    tmp = g_strdup_printf ("%d", purple_conv_chat_get_id (chat));
    child_child = xmlnode_new_child (child, "chatroom_id");
    xmlnode_insert_data (child_child, tmp, -1);

    child_child = xmlnode_new_child (child, "virtual_server");
    xmlnode_insert_data (child_child, hostname, -1);

    tmp = g_strdup_printf ("%d", conversation_stat);
    child_child = xmlnode_new_child (child, "security_stat");
    xmlnode_insert_data (child_child, tmp, -1);

    for (child = xmlnode_get_child (node, "chatinfo"); child != NULL;
         child = xmlnode_get_next_twin (child)) {

      gchar *account_name;

      child_child = xmlnode_get_child (child, "account");
      account_name = xmlnode_get_data (child_child);

      gchar *virtual_server;

      child_child = xmlnode_get_child (child, "virtual_server");
      virtual_server = xmlnode_get_data (child_child);


      if (account_name != NULL && virtual_server != NULL) {

        if (!strcmp (account_name, accountname)
            && !strcmp (virtual_server, hostname)) {
          g_free (virtual_server);
          g_free (account_name);
          continue;
        } else {

          if (strlen (virtual_server) > 0) {
            xmlnode *node_clone = xmlnode_copy (child);

            xmlnode_insert_child (new_node, node_clone);
          }
          g_free (virtual_server);
          g_free (account_name);
          continue;
        }
      }
      g_free (virtual_server);
      g_free (account_name);
    }

    data = xmlnode_to_formatted_str (new_node, NULL);
    purple_util_write_data_to_file ("otr.chatinfo.xml", data, -1);

    g_free (data);
    xmlnode_free (new_node);
    xmlnode_free (node);
  }
  return 1;

}


int
find_chat_id (const char *accountname)
{
  xmlnode *node, *child, *child_child;

//const gchar *chatinfofile = g_strdup_printf("%s.xml", CHATINFOFILENAME);
  gchar *tmp;

  if ((node =
       purple_util_read_xml_from_file ("otr.chatinfo.xml",
                                       "chat room info")) != NULL) {
    for (child = xmlnode_get_child (node, "chatinfo"); child != NULL;
         child = xmlnode_get_next_twin (child)) {
      gchar *account_name;

      child_child = xmlnode_get_child (child, "account");
      account_name = xmlnode_get_data (child_child);
      if (!strcmp (account_name, accountname)) {
        child_child = xmlnode_get_child (child, "chatroom_id");
        tmp = xmlnode_get_data (child_child);
        xmlnode_free (node);
        g_free (account_name);
        return atoi (tmp);
      }
      g_free (account_name);
    }
  } else {
    xmlnode_free (node);
    return -1;
  }
  return -1;
}

TrustLevel
find_chat_conv_stat (const char *accountname)
{

  xmlnode *node, *child, *child_child;

//const gchar *chatinfofile = g_strdup_printf("%s.xml", CHATINFOFILENAME);
  gchar *tmp;

  if ((node =
       purple_util_read_xml_from_file ("otr.chatinfo.xml",
                                       "chat room info")) != NULL) {
    for (child = xmlnode_get_child (node, "chatinfo"); child != NULL;
         child = xmlnode_get_next_twin (child)) {
      gchar *account_name;

      child_child = xmlnode_get_child (child, "account");
      account_name = xmlnode_get_data (child_child);
      if (!strcmp (account_name, accountname)) {
        child_child = xmlnode_get_child (child, "security_stat");
        tmp = xmlnode_get_data (child_child);
        xmlnode_free (node);
        return atoi (tmp);
      }
      g_free (account_name);
    }
  } else {
    xmlnode_free (node);
    return -1;
  }
  return -1;
}


gchar *
find_chat_host (const gchar * accountname)
{

  xmlnode *node, *child, *child_child;

//const gchar *chatinfofile = g_strdup_printf("%s.xml", CHATINFOFILENAME);
  gchar *tmp;

  if ((node =
       purple_util_read_xml_from_file ("otr.chatinfo.xml",
                                       "chat room info")) != NULL) {
    for (child = xmlnode_get_child (node, "chatinfo"); child != NULL;
         child = xmlnode_get_next_twin (child)) {
      gchar *account_name;

      child_child = xmlnode_get_child (child, "account");
      account_name = xmlnode_get_data (child_child);
      if (!strcmp (account_name, accountname)) {
        child_child = xmlnode_get_child (child, "virtual_server");
        tmp = xmlnode_get_data (child_child);
        xmlnode_free (node);
        return tmp;
      }
      g_free (account_name);
    }
  } else {
    xmlnode_free (node);
    return NULL;
  }
  return NULL;

}


/* Send an IM from the given account to the given recipient.  Display an
 * error dialog if that account isn't currently logged in. */
void
otrg_plugin_inject_message (PurpleAccount * account,
                            const char *recipient, const char *message)
{
  PurpleConnection *connection;

  connection = purple_account_get_connection (account);
  if (!connection) {
    const char *protocol = purple_account_get_protocol_id (account);
    const char *accountname = purple_account_get_username (account);
    PurplePlugin *p = purple_find_prpl (protocol);
    char *msg =
      g_strdup_printf ("You are not currently connected to "
                       "account %s (%s).", accountname,
                       (p && p->info->name) ? p->info->name : "Unknown");

    otrg_dialog_notify_error (accountname, protocol, recipient,
                              "Not connected", msg, NULL);
    g_free (msg);
    return;
  }
  serv_send_im (connection, recipient, message, 0);

/*
 * gchar *message_clone = g_strdup(message);
 * 
 * gchar *md5_of_message = md5_encode(message_clone);
 * 
 * gchar *md5message =
 * g_strdup_printf("?MD5?%s?ENDMD5?\t?U_ID?%ld?ENDU_ID?",
 * md5_of_message, timestamp
 * );
 */
}

void
otrg_plugin_chat_inject_message (PurpleAccount * account,
                                 const char *recipient,
                                 const char *real_sender, const char *message)
{

  PurpleConnection *connection = purple_account_get_connection (account);

  if (!connection) {
    const char *protocol = purple_account_get_protocol_id (account);
    const char *accountname = purple_account_get_username (account);
    PurplePlugin *p = purple_find_prpl (protocol);
    char *msg =
      g_strdup_printf ("You are not currently connected to "
                       "account %s (%s).", accountname,
                       (p && p->info->name) ? p->info->name : "Unknown");

    otrg_dialog_notify_error (accountname, protocol, recipient,
                              "Not connected", msg, NULL);
    g_free (msg);
    return;
  }

  int chat_id = find_chat_id (purple_account_get_username (account));
  PurpleConvChat *chat =
    PURPLE_CONV_CHAT (purple_find_chat (connection, chat_id));

  if (!chat) {
    purple_debug_info ("otrCHAT",
                       "[otrg_plugin_chat_inject_message] can not find chat!! \n");
    const char *protocol = purple_account_get_protocol_id (account);
    const char *accountname = purple_account_get_username (account);

    purple_debug_info ("otrCHAT",
                       "[otrg_plugin_chat_inject_message][accountname]:%s \n",
                       accountname);
    purple_debug_info ("otrCHAT",
                       "[otrg_plugin_chat_inject_message][recipient]:%s \n",
                       recipient);
  //PurplePlugin *p = purple_find_prpl (protocol);
    char *msg =
      g_strdup_printf ("[%s] You are not currently in any chatroom!",
                       accountname);

    otrg_dialog_notify_error (accountname, protocol, recipient,
                              "Not in chat room", msg, NULL);
    g_free (msg);
    return;
  } else {
    GTimeVal now;

  /*
   * typedef struct {
   * glong tv_sec;
   * glong tv_usec;
   * } GTimeVal;
   */
    g_get_current_time (&now); //timestamp->

    glong timestamp = now.tv_usec; //use microseconds to be more precisely

    gchar *newmessage =
      g_strdup_printf
      ("?RECV?%s?ENDRECV?\t?SEND?%s?ENDSEND?\t?U_ID?%ld?ENDU_ID?\t%s",
       recipient,
       real_sender,
       timestamp,
       message);

  //purple_debug_info ("otrCHAT", "[otrg_plugin_chat_inject_message]%s\n",newmessage);
    serv_chat_send (connection, purple_conv_chat_get_id (chat),
                    newmessage, PURPLE_MESSAGE_SEND);

  //generate the md5 value for this message along with the timestamp
    g_free (newmessage);

    gchar *message_clone = g_strdup (message);

    gchar *md5_of_message = md5_encode (message_clone);

    gchar *md5message =
      g_strdup_printf ("?MD5?%s?ENDMD5?\t?U_ID?%ld?ENDU_ID?",
                       md5_of_message, timestamp);

    serv_chat_send (connection, purple_conv_chat_get_id (chat),
                    md5message, PURPLE_MESSAGE_SEND);
    purple_debug_info ("MD5TEST", "[MD5CODE]:%s \n", md5message);
    g_free (message_clone);
    g_free (md5_of_message);
  }
}


static OtrlPolicy
policy_cb (void *opdata, ConnContext * context)
{
  PurpleAccount *account;
  OtrlPolicy policy = OTRL_POLICY_DEFAULT;

  if (!context)
    return policy;
  account = purple_accounts_find (context->accountname, context->protocol);
  if (!account)
    return policy;
  return otrg_ui_find_policy (account, context->username);
}

static const char *
protocol_name_cb (void *opdata, const char *protocol)
{
  PurplePlugin *p = purple_find_prpl (protocol);

  if (!p)
    return NULL;
  return p->info->name;
}

static void
protocol_name_free_cb (void *opdata, const char *protocol_name)
{

/*
 * Do nothing, since we didn't actually allocate any memory in
 * * protocol_name_cb. 
 */
}

/* Generate a private key for the given accountname/protocol */
void
otrg_plugin_create_privkey (const char *accountname, const char *protocol)
{
  OtrgDialogWaitHandle waithandle;
  gchar *privkeyfile =
    g_build_filename (purple_user_dir (), PRIVKEYFNAME, NULL);
  if (!privkeyfile) {
    fprintf (stderr, "Out of memory building filenames!\n");
    return;
  }

  waithandle = otrg_dialog_private_key_wait_start (accountname, protocol);

/*
 * Generate the key 
 */
  otrl_privkey_generate (otrg_plugin_userstate, privkeyfile,
                         accountname, protocol);
  g_free (privkeyfile);
  otrg_ui_update_fingerprint ();

/*
 * Mark the dialog as done. 
 */
  otrg_dialog_private_key_wait_done (waithandle);
}

static void
create_privkey_cb (void *opdata, const char *accountname,
                   const char *protocol)
{
  otrg_plugin_create_privkey (accountname, protocol);
}

static int
is_logged_in_cb (void *opdata, const char *accountname,
                 const char *protocol, const char *recipient)
{
  PurpleAccount *account;
  PurpleBuddy *buddy;

  account = purple_accounts_find (accountname, protocol);
  if (!account)
    return -1;
  buddy = purple_find_buddy (account, recipient);
  if (!buddy)
    return -1;
#if PURPLE_MAJOR_VERSION < 2
  return (buddy->present == PURPLE_BUDDY_ONLINE);
#else
  return (PURPLE_BUDDY_IS_ONLINE (buddy));
#endif
}

static void
inject_message_cb (void *opdata, const char *accountname,
                   const char *protocol, const char *recipient,
                   const char *message)
{
  PurpleAccount *account = purple_accounts_find (accountname, protocol);

  if (!account) {
    PurplePlugin *p = purple_find_prpl (protocol);
    char *msg = g_strdup_printf ("Unknown account %s (%s).",
                                 accountname,
                                 (p
                                  && p->info->name) ? p->info->
                                 name : "Unknown");
    otrg_dialog_notify_error (accountname, protocol, recipient,
                              "Unknown account", msg, NULL);
    g_free (msg);
    return;
  }

  PurpleConnection *connection = purple_account_get_connection (account);
  PurpleConvChat *chat =
    PURPLE_CONV_CHAT (purple_find_chat
                      (connection, find_chat_id (accountname)));
  if (!chat) {
    otrg_plugin_inject_message (account, recipient, message);
  } else {
    otrg_plugin_chat_inject_message (account, recipient, accountname,
                                     message);
  }
}

static void
notify_cb (void *opdata, OtrlNotifyLevel level,
           const char *accountname, const char *protocol,
           const char *username, const char *title,
           const char *primary, const char *secondary)
{
  PurpleNotifyMsgType purplelevel = PURPLE_NOTIFY_MSG_ERROR;

  switch (level) {
    case OTRL_NOTIFY_ERROR:
      purplelevel = PURPLE_NOTIFY_MSG_ERROR;
      break;
    case OTRL_NOTIFY_WARNING:
      purplelevel = PURPLE_NOTIFY_MSG_WARNING;
      break;
    case OTRL_NOTIFY_INFO:
      purplelevel = PURPLE_NOTIFY_MSG_INFO;
      break;
  }

  otrg_dialog_notify_message (purplelevel, accountname, protocol,
                              username, title, primary, secondary);
}

static int
display_otr_message_cb (void *opdata, const char *accountname,
                        const char *protocol,
                        const char *username, const char *msg)
{
  return otrg_dialog_display_otr_message (accountname, protocol,
                                          username, msg);
}

static void
update_context_list_cb (void *opdata)
{
  otrg_ui_update_keylist ();
}

static void
confirm_fingerprint_cb (void *opdata, OtrlUserState us,
                        const char *accountname,
                        const char *protocol,
                        const char *username, unsigned char fingerprint[20])
{
  otrg_dialog_unknown_fingerprint (us, accountname, protocol,
                                   username, fingerprint);
}

static void
write_fingerprints_cb (void *opdata)
{
  otrg_plugin_write_fingerprints ();
}

static void
gone_secure_cb (void *opdata, ConnContext * context)
{
  purple_debug_info ("otrCHAT", "[gone_secure], Do something\n");

  otrg_dialog_connected (context);
}

static void
gone_insecure_cb (void *opdata, ConnContext * context)
{
  purple_debug_info ("otrCHAT", "[gone_insecure], Do something\n");
  otrg_dialog_disconnected (context);
}

static void
still_secure_cb (void *opdata, ConnContext * context, int is_reply)
{
  purple_debug_info ("otrCHAT", "[still], %s\n", context->accountname);
  otrg_dialog_stillconnected (context);
// if (is_reply == 0)
// {
//  purple_debug_info ("otrCHAT","[still_secure], %s\n",context->accountname);
//  otrg_dialog_stillconnected (context);
// }
}

static void
log_message_cb (void *opdata, const char *message)
{
  purple_debug_info ("otrCHATlib", message);
}

static OtrlMessageAppOps ui_ops = {
  policy_cb,
  create_privkey_cb,
  is_logged_in_cb,
  inject_message_cb,
  notify_cb,
  display_otr_message_cb,
  update_context_list_cb,
  protocol_name_cb,
  protocol_name_free_cb,
  confirm_fingerprint_cb,
  write_fingerprints_cb,
  gone_secure_cb,
  gone_insecure_cb,
  still_secure_cb,
  log_message_cb
};
static void
process_sending_im (PurpleAccount * account, char *who, char **message,
                    void *m)
{

  char *newmessage = NULL;
  const char *accountname = purple_account_get_username (account);

  const char *protocol = purple_account_get_protocol_id (account);
  char *username;
  gcry_error_t err;

  if (!who || !message || !*message)
    return;
  username = strdup (purple_normalize (account, who));
  err = otrl_message_sending (otrg_plugin_userstate, &ui_ops, NULL,
                              accountname, protocol, username,
                              *message, NULL, &newmessage, NULL, NULL);
  if (err && newmessage == NULL) {
  /*
   * Be *sure* not to send out plaintext 
   */
    char *ourm = strdup ("");

    free (*message);
    *message = ourm;
  } else if (newmessage) {
    char *ourm = malloc (strlen (newmessage) + 1);

    if (ourm) {
      strcpy (ourm, newmessage);
    }
    otrl_message_free (newmessage);
    free (*message);
    *message = ourm;
  }
  free (username);
}

//[add] chat sending
static void
process_sending_chat (PurpleAccount * account, char **message, int id)
{
  const char *message_cp = strdup (*message);

  PurpleConnection *connection = purple_account_get_connection (account);

  const gchar *accountname = purple_account_get_username (account);

  const gchar *protocol = purple_account_get_protocol_id (account);

  PurpleConversation *conv = purple_find_chat (connection, id);

  PurpleConvChat *chat = PURPLE_CONV_CHAT (conv);

  const gchar *hostname =
    find_chat_host (purple_account_get_username (account));


  GList *l = purple_conv_chat_get_users (chat);

  gcry_error_t err;

  if (!message || !*message)
    return;

  if (hostname) {
  //if this account is the virtual server, it will broadcast the message
    if (!g_ascii_strcasecmp (hostname, accountname)) {

      char *newmessage = NULL;

      for (; l != NULL; l = l->next) {

        char *username = strdup (((PurpleConvChatBuddy *) l->data)->name);

        if (g_ascii_strcasecmp (username, accountname)) {

          err = otrl_message_sending
            (otrg_plugin_userstate, &ui_ops, NULL,
             accountname, protocol,
             username, message_cp, NULL, &newmessage, NULL, NULL);


          if (err && newmessage == NULL) {
          /*
           * Be *sure* not to send out plaintext 
           */
            char *ourm = strdup ("");

            free (*message);
            *message = ourm;
          } else if (newmessage) {

            purple_debug_info ("otrCHAT", "%s\n", newmessage);

            char *ourm = malloc (strlen (newmessage) + 1);

            if (ourm) {
              strcpy (ourm, newmessage);
            }
            otrl_message_free (newmessage);
            free (*message);
            *message = ourm;
            otrg_plugin_chat_inject_message
              (account,
               ((PurpleConvChatBuddy *) l->data)->name, hostname, *message);

          }

        }
      //free(newmessage);

      }
    }
  //if the account is not the server, it just need to send the message to the server.
    else {
      char *newmessage = NULL;


      err = otrl_message_sending
        (otrg_plugin_userstate, &ui_ops, NULL,
         accountname, protocol,
         hostname, *message, NULL, &newmessage, NULL, NULL);
      if (err && newmessage == NULL) {
      /*
       * Be *sure* not to send out plaintext 
       */
        char *ourm = strdup ("");

        free (*message);
        *message = ourm;
      } else if (newmessage) {
        char *ourm = malloc (strlen (newmessage) + 1);

        if (ourm) {
          strcpy (ourm, newmessage);
        }
        otrl_message_free (newmessage);
        free (*message);
        *message = ourm;

        otrg_plugin_chat_inject_message (account, hostname, accountname,
                                         *message);

      }
    }

  //clean up the buffer.
    char *ourmnull = strdup ("");

    free (*message);
    *message = ourmnull;

  }
//purple_conversation_write() is used to write to local screen, it won't send out any messages to the network.
  purple_conversation_write (conv, accountname, message_cp,
                             PURPLE_MESSAGE_RECV, time (NULL));

}



//[end_add]

/* Send the default OTR Query message to the correspondent of the given
 * context, from the given account.  [account is actually a
 * PurpleAccount*, but it's declared here as void* so this can be passed
 * as a callback.] */
void
otrg_plugin_send_default_query (ConnContext * context, void *vaccount)
{
  PurpleAccount *account = vaccount;


  char *msg = otrl_proto_default_query_msg (context->accountname,
                                            otrg_ui_find_policy (account,
                                                                 context->
                                                                 username));


  int chat_id = find_chat_id (purple_account_get_username (account));

  if (chat_id != -1) {
    otrg_plugin_chat_inject_message
      (account, context->username, context->accountname,
       msg ? msg : "?OTRv2?");
  } else {

    otrg_plugin_inject_message (account,
                                context->username, msg ? msg : "?OTRv2?");
  }
  free (msg);
}

/* Send the default OTR Query message to the correspondent of the given
 * conversation. */
void
otrg_plugin_send_default_query_conv (PurpleConversation * conv)
{
  PurpleAccount *account;
  const char *username, *accountname;
  char *msg;

  account = purple_conversation_get_account (conv);
  accountname = purple_account_get_username (account);
  username = purple_conversation_get_name (conv);
//purple_debug_info ("otrCHAT", "[accountname]: %s\n", accountname);
//purple_debug_info ("otrCHAT", "[username]: %s\n", username);
  msg = otrl_proto_default_query_msg
    (accountname, otrg_ui_find_policy (account, username));
//show the query message in the debug window
//purple_debug_info ("otrCHAT", "[OTR Query Message %s]\n", msg);
  otrg_plugin_inject_message (account, username, msg ? msg : "?OTRv2?");
  free (msg);
}

//[add]

/* Send the CHAT OTR Query message to the correspondent of the given
 * conversation. initate of a otr chat conversation */
void
otrg_plugin_send_chat_query_conv (PurpleConversation * conv)
{
  PurpleAccount *account;
  PurpleConvChat *chat = PURPLE_CONV_CHAT (conv);
  GList *l = purple_conv_chat_get_users (chat); //this should be a userlist!!!
  const char *chatroomname, *accountname;

//const int chatroomid = purple_conv_chat_get_id (chat);
//[tag]send_chat_query
  account = purple_conversation_get_account (conv);
//this is the account who sent
//out the message, which means who own the current chat window. This is A!!!
  accountname = purple_account_get_username (account);
//title of the chat room
  chatroomname = purple_conversation_get_name (conv);


  purple_debug_info ("otrCHAT",
                     "[send query][accountname]: %s \n", accountname);

  find_replace_account_chat_info (accountname,
                                  accountname, chat, TRUST_PRIVATE);

/*
 * Users and finding user's policy 
 */
//use OTRL_POLICY_DEFAULT now, but should support different policy
  for (; l != NULL; l = l->next) {
    if (!strcmp (((PurpleConvChatBuddy *) l->data)->name, accountname))
      continue;    // don't send the query message to self
    char *msg;

    purple_debug_info ("otrCHAT", "[begin Query Message]:\n");
    purple_debug_info ("otrCHAT",
                       "[clients]: %s\n",
                       ((PurpleConvChatBuddy *) l->data)->name);

    msg = otrl_proto_default_query_msg
      (accountname,
       otrg_ui_find_policy (account,
                            ((PurpleConvChatBuddy *) l->data)->name));
    purple_debug_info ("otrCHAT", "[messages]: %s\n", msg);

    otrg_plugin_chat_inject_message (account,
                                     ((PurpleConvChatBuddy *) l->
                                      data)->name, accountname,
                                     msg ? msg : "?OTRv2?");

    purple_debug_info ("otrCHAT", "[end Query Message]:\n");
    free (msg);
  //use OTRL_POLICY_DEFAULT now, but should support different policy
  /*
   * if (((PurpleConvChatBuddy *)l->data)->name == accountname)
   * * {
   * * //do nothing if the username == accountname
   * * }else {
   * * 
   * * 
   * * 
   * * char *msg;
   * * msg = otrl_proto_default_query_msg(accountname, otrg_ui_find_policy(account, ((PurpleConvChatBuddy *)l->data)->name));
   * * purple_debug_info("otrCHAT", "OTR Query Message: %s\n", msg);
   * * otrg_plugin_chat_inject_message(conv, msg ? msg : "?OTRv2?");
   * * one chat room should just one request message
   * * or should i send every one an im message???
   * * free(msg);
   * * } 
   */
  }


}

//[end_add]

static gboolean
process_receiving_im (PurpleAccount * account,
                      char **who, char **message, int *flags, void *m)
{
  MessageTaged *messageArr;

  messageArr = parse_message (*message);

  purple_debug_info ("MD5TEST", "[md5codeman][im]:%s, %ld, %s\n",
                     get_message_type_name (messageArr->type),
                     messageArr->uniqueID, messageArr->md5sum);

  const char *accountname = purple_account_get_username (account);
  char *newmessage = NULL;
  OtrlTLV *tlvs = NULL;
  OtrlTLV *tlv = NULL;
  char *username;
  gboolean res;

  const char *protocol;

  if (!who || !*who || !message || !*message)
    return 0;
  username = strdup (purple_normalize (account, *who));

  protocol = purple_account_get_protocol_id (account);
//call the libotr function [otrl_message_receiving] to handle the received message
  res = otrl_message_receiving
    (otrg_plugin_userstate, &ui_ops, NULL,
     accountname, protocol, username,
     *message, &newmessage, &tlvs, NULL, NULL);
  if (newmessage) {
    char *ourm = malloc (strlen (newmessage) + 1);

    if (ourm) {
      strcpy (ourm, newmessage);
    }
    otrl_message_free (newmessage);
    free (*message);
    *message = ourm;
  }

  tlv = otrl_tlv_find (tlvs, OTRL_TLV_DISCONNECTED);
  if (tlv) {
  /*
   * Notify the user that the other side disconnected. 
   */
    otrg_dialog_finished (accountname, protocol, username);
    otrg_ui_update_keylist ();
  }

  otrl_tlv_free (tlvs);
  free (username);

/*
 * If we're supposed to ignore this incoming message (because it's a
 * * protocol message), set it to NULL, so that other plugins that
 * * catch receiving-im-msg don't return 0, and cause it to be
 * * displayed anyway. 
 */
  if (res) {
    free (*message);
    *message = NULL;
  }


/*
 * verify the md5 code of this message
 */
  if (*message != NULL && messageArr->type == MD5CODE) {
  /*
   * for MD5 messages *who is the real sender and this acountname is the receiver 
   */
    MessageMD5 *messageMD5 = NULL;

    messageMD5 = g_new0 (MessageMD5, 1);
    messageMD5->rsender = g_strdup (*who);
    messageMD5->rreceiver = g_strdup (accountname);
    messageMD5->uniqueID = messageArr->uniqueID;
    messageMD5->md5sum = messageArr->md5sum;

    purple_debug_info ("MD5TEST", "receiver => %s\n", messageMD5->rreceiver);

    md5_verified (messageMD5);

    free (*message);
    *message = NULL;

  }

  return res;
}

//[add]call this function when receiving a chat message
static gboolean
process_receiving_chat (PurpleAccount * account,
                        char **sender,
                        char **message, PurpleConversation * conv, int *flags)
{

  MessageTaged *messageArr;

  messageArr = parse_message (*message);

  purple_debug_info ("otrCHAT", "[md5codeman][chat]:%s, %ld, %s, %s\n",
                     get_message_type_name (messageArr->type),
                     messageArr->uniqueID, messageArr->md5sum,
                     messageArr->message);

  const char *accountname = purple_account_get_username (account);

  if (!g_ascii_strcasecmp (*sender, accountname)) {
    free (*message);
    *message = NULL;
    return 1;
  }

  if (purple_conversation_get_type (conv) == PURPLE_CONV_TYPE_CHAT) {
    PurpleConvChat *chat = PURPLE_CONV_CHAT (conv);
    GList *l = purple_conv_chat_get_users (chat);
    gint usercount = 0;

    for (; l != NULL; l = l->next) {
      usercount++;
    }
    purple_debug_info ("otrCHAT",
                       "[process_receiving_chat][usernumbers]:%d\n",
                       usercount);
  //only two persons in the chat room
    if (usercount < 3) {
      char *newmessage = NULL;
      OtrlTLV *tlvs = NULL;
      OtrlTLV *tlv = NULL;
      char *username;
      gboolean res;

      const char *protocol;

      if (!sender || !*sender || !message || !*message)
        return 0;
      username = strdup (purple_normalize (account, *sender));

      protocol = purple_account_get_protocol_id (account);
    //call the libotr function [otrl_message_receiving] to handle the received message
      res = otrl_message_receiving
        (otrg_plugin_userstate, &ui_ops, NULL,
         accountname, protocol, username,
         *message, &newmessage, &tlvs, NULL, NULL);
      if (newmessage) {
        char *ourm = malloc (strlen (newmessage) + 1);

        if (ourm) {
          strcpy (ourm, newmessage);
        }
        otrl_message_free (newmessage);
        free (*message);
        *message = ourm;
      }

      tlv = otrl_tlv_find (tlvs, OTRL_TLV_DISCONNECTED);
      if (tlv) {
      /*
       * Notify the user that the other side disconnected. 
       */
        otrg_dialog_finished (accountname, protocol, username);
        otrg_ui_update_keylist ();
      }

      otrl_tlv_free (tlvs);
      free (username);
    /*
     * If we're supposed to ignore this incoming message (because it's a
     * * protocol message), set it to NULL, so that other plugins that
     * * catch receiving-im-msg don't return 0, and cause it to be
     * * displayed anyway. 
     */

      if (res) {
        free (*message);
        *message = NULL;
      }



      return res;

    } else {

      char *message_tmp = g_strdup_printf ("%s", *message);

      char *tag_recv_begin = NULL;

      tag_recv_begin = strstr (message_tmp, "?RECV?");

    //check if the message has tag or not.
      if (tag_recv_begin) {
        *tag_recv_begin = '\0';
      } else {

      //<tag>
      //update the otr.chatinof file
      //should this be here?
      //<fix>
        find_replace_account_chat_info (accountname,
                                        find_chat_host (accountname),
                                        PURPLE_CONV_CHAT (conv),
                                        TRUST_PRIVATE);

        char *newmessage = NULL;
        OtrlTLV *tlvs = NULL;
        OtrlTLV *tlv = NULL;
        char *username;
        gboolean res;

        const char *protocol;

        if (!sender || !*sender || !message || !*message)
          return 0;
        username = strdup (purple_normalize (account, *sender));

        protocol = purple_account_get_protocol_id (account);
      //call the libotr function [otrl_message_receiving] to handle the received message
        res = otrl_message_receiving
          (otrg_plugin_userstate, &ui_ops, NULL,
           accountname, protocol, username,
           *message, &newmessage, &tlvs, NULL, NULL);
        if (newmessage) {
          char *ourm = malloc (strlen (newmessage) + 1);

          if (ourm) {
            strcpy (ourm, newmessage);
          }
          otrl_message_free (newmessage);
          free (*message);
          *message = ourm;
        }

        tlv = otrl_tlv_find (tlvs, OTRL_TLV_DISCONNECTED);
        if (tlv) {
        /*
         * Notify the user that the other side disconnected. 
         */
          otrg_dialog_finished (accountname, protocol, username);
          otrg_ui_update_keylist ();
        }

        otrl_tlv_free (tlvs);
        free (username);
      /*
       * If we're supposed to ignore this incoming message (because it's a
       * * protocol message), set it to NULL, so that other plugins that
       * * catch receiving-im-msg don't return 0, and cause it to be
       * * displayed anyway. 
       */
        if (res) {
          free (*message);
          *message = NULL;
        }
        purple_debug_info ("otrCHAT",
                           "[otr_message]%s->%s, %s\n",
                           *sender, accountname, *message);
        return res;


      //<tag>
      //char *message_not_encrypted = g_strdup_printf ("<b>This coming message from</b> <i>%s</i> <b>probably is not encrypted.</b>\n <b>Message:</b>[%s]\n <b>Please inform him/her to setup the latest OTR.</b>", *sender,*message);
      //should i call otrg_gtk_dialog_display_otr_message() will be better?
      // purple_conversation_write (conv, NULL, message_not_encrypted,  PURPLE_MESSAGE_SYSTEM,
      //    time (NULL));
      // return 0;
      }

      free (*message);
      *message = g_strdup_printf ("%s", message_tmp);

      char *receiver = NULL;

      receiver = tag_recv_begin + 6;

      char *tag_recv_end = NULL;

      tag_recv_end = strstr (receiver, "?ENDRECV?\t");

      *tag_recv_end = '\0';

      char *real_sender = NULL;

      real_sender = tag_recv_end + 16;

      char *tag_send_end = NULL;

      tag_send_end = strstr (real_sender, "?ENDSEND?\t");

      *tag_send_end = '\0';

      char *uid = NULL;

      uid = tag_send_end + 16;

      char *tag_uid_end = NULL;

      tag_uid_end = strstr (uid, "?ENDU_ID?\t");

      *tag_uid_end = '\0';

      *message = g_strconcat (*message, (tag_uid_end + 10), NULL);

      purple_debug_info ("otrCHAT",
                         "[process_receiving_chat][tag]:%s, %s, %s\n",
                         receiver, real_sender, *message);

      if (g_ascii_strcasecmp (receiver, accountname)) {
        free (*message);
        *message = NULL;
        return 1;
      }
    //if this is the initate message, create the otr.chatinfo file fro this account
      if (strstr (*message, "?OTR?v2?")) {
      //TODO: now it will set it to be trust level at beginning, but it is not always true.
      //Maybe the initiation of the keys crashed, the trust level should be not private.
      //Another situation is that the signatures have not been verified, so should set to not verified level.
        find_replace_account_chat_info
          (accountname, *sender, PURPLE_CONV_CHAT (conv), TRUST_PRIVATE);
      }

      const gchar *hostname = find_chat_host (accountname);

      char *newmessage = NULL;
      OtrlTLV *tlvs = NULL;
      OtrlTLV *tlv = NULL;
      char *sender_username;
      gboolean res;
      const char *protocol;

      if (!sender || !*sender || !message || !*message) {
        purple_debug_info ("otrCHAT",
                           "[process_receiving_chat]get in here %s\n",
                           "!sender || !*sender || !message || !*message");
        return 0;
      }

      sender_username = strdup (purple_normalize (account, *sender));
      accountname = purple_account_get_username (account);
      protocol = purple_account_get_protocol_id (account);

      res = otrl_message_receiving
        (otrg_plugin_userstate, &ui_ops, NULL,
         accountname, protocol,
         sender_username, *message, &newmessage, &tlvs, NULL, NULL);

      if (newmessage) {
        char *ourm = malloc (strlen (newmessage) + 1);

        if (ourm) {
          strcpy (ourm, newmessage);
        }
        otrl_message_free (newmessage);
        free (*message);
        *message = ourm;

      //the virtual server is responsible for retransfer the message
        if (!g_ascii_strcasecmp (accountname, hostname)) {
          gcry_error_t err;
          GList *l = purple_conv_chat_get_users (PURPLE_CONV_CHAT (conv));

          for (; l != NULL; l = l->next) {

            char *newmessage_send = NULL;

            if (g_ascii_strcasecmp
                (((PurpleConvChatBuddy *) l->data)->name, hostname)
                &&
                g_ascii_strcasecmp (((PurpleConvChatBuddy *) l->data)->
                                    name, real_sender)) {

              err = otrl_message_sending
                (otrg_plugin_userstate,
                 &ui_ops, NULL, accountname,
                 protocol,
                 ((PurpleConvChatBuddy *) l->
                  data)->name, *message, NULL, &newmessage_send, NULL, NULL);


              if (err && newmessage_send == NULL) {
              /*
               * Be *sure* not to send out plaintext 
               */
                char *ourm_send = strdup ("");

                free (*message);
                *message = ourm_send;
              } else if (newmessage_send) {
                char *ourm_send = malloc (strlen (newmessage_send) + 1);

                if (ourm_send) {
                  strcpy (ourm_send, newmessage_send);
                }
                otrl_message_free (newmessage_send);

                char *message_send = NULL;

                message_send = ourm_send;

                otrg_plugin_chat_inject_message
                  (account,
                   ((PurpleConvChatBuddy
                     *) l->data)->name, real_sender, message_send);
                free (message_send);

              }
            }
          }
        }

        free (*sender);
        char *ourm_sender = strdup (real_sender);

        *sender = ourm_sender;

        purple_debug_info ("otrCHAT",
                           "[process_receiving_chat][resending][sender][%s]\n",
                           *sender);

      }

      tlv = otrl_tlv_find (tlvs, OTRL_TLV_DISCONNECTED);
      if (tlv) {
      /*
       * Notify the user that the other side disconnected. 
       */
        otrg_dialog_finished (accountname, protocol, sender_username);
        otrg_ui_update_keylist ();
      }

      otrl_tlv_free (tlvs);
      free (sender_username);
    /*
     * If we're supposed to ignore this incoming message (because it's a
     * * protocol message), set it to NULL, so that other plugins that
     * * catch receiving-im-msg don't return 0, and cause it to be
     * * displayed anyway. 
     */
      if (res) {
        free (*message);
        *message = NULL;
      }

    /*
     * verify the md5 code of this message
     */
      if (*message != NULL && messageArr->type == MD5CODE) {
      /*
       * for MD5 messages *who is the real sender and this acountname is the receiver 
       */
        MessageMD5 *messageMD5 = NULL;

        messageMD5 = g_new0 (MessageMD5, 1);
        messageMD5->rsender = g_strdup (sender_username);
        messageMD5->rreceiver = g_strdup (accountname);
        messageMD5->uniqueID = messageArr->uniqueID;
        messageMD5->md5sum = messageArr->md5sum;

        purple_debug_info ("MD5TEST", "receiver => %s\n",
                           messageMD5->rreceiver);

        md5_verified (messageMD5);

        free (*message);
        *message = NULL;

      }



      return res;
    }
  } else {
  //this should never happen
    return 0;
  }

}



static void
process_chat_left (PurpleConversation * conv)
{
  if (conv) {
    PurpleAccount *account = purple_conversation_get_account (conv);
    const char *accountname = purple_account_get_username (account);

    purple_debug_info ("otrCHAT", "[process_chat_left]%s\n", accountname);

    const char *hostname = find_chat_host (accountname);

    if (!g_ascii_strcasecmp (accountname, hostname)) {
    //gcry_error_t err;
      GList *l = purple_conv_chat_get_users (PURPLE_CONV_CHAT (conv));

      for (; l != NULL; l = l->next) {

        if (g_ascii_strcasecmp
            (((PurpleConvChatBuddy *) l->data)->name, hostname)) {
          ConnContext *context = otrg_plugin_chatuser_conv_to_context (conv,
                                                                       ((PurpleConvChatBuddy *) l->data)->name);

          if (context)
            otrl_message_disconnect (otrg_plugin_userstate, &ui_ops, NULL,
                                     context->accountname, context->protocol,
                                     context->username);
        }
      }
    } else {
      ConnContext *context =
        otrg_plugin_chatuser_conv_to_context (conv, hostname);
      if (context)
        otrl_message_disconnect (otrg_plugin_userstate, &ui_ops, NULL,
                                 context->accountname, context->protocol,
                                 context->username);
    }

  }
}

static void
process_chat_buddy_joined (PurpleConversation * conv, const char *name,
                           PurpleConvChatBuddyFlags flags,
                           gboolean new_arrival)
{
  if (conv) {
    PurpleAccount *account = purple_conversation_get_account (conv);
    const char *accountname = purple_account_get_username (account);
    const char *hostname = find_chat_host (accountname);
    TrustLevel level = find_chat_conv_stat (accountname);

    find_replace_account_chat_info (accountname, hostname,
                                    PURPLE_CONV_CHAT (conv),
                                    level ? level : TRUST_NOT_PRIVATE);
  }
}




static void
process_conv_create (PurpleConversation * conv, void *data)
{
  if (conv) {
  //is this neccessary?
    if (purple_conversation_get_type (conv) == PURPLE_CONV_TYPE_CHAT) {
      PurpleAccount *account = purple_conversation_get_account (conv);
      const char *accountname = purple_account_get_username (account);

      find_replace_account_chat_info (accountname,
                                      find_chat_host (accountname),
                                      PURPLE_CONV_CHAT (conv),
                                      find_chat_conv_stat (accountname) ?
                                      find_chat_conv_stat (accountname) :
                                      TRUST_NOT_PRIVATE);
      purple_debug_info ("otrCHAT", "[process_conv_create]\n");

    }


    otrg_dialog_new_conv (conv);
  }
}

static void
process_connection_change (PurpleConnection * conn, void *data)
{

/*
 * If we log in or out of a connection, make sure all of the OTR
 * * buttons are in the appropriate sensitive/insensitive state. 
 */
  otrg_dialog_resensitize_all ();
}

#if PURPLE_MAJOR_VERSION < 2

/* purple-2.0.0 no longer has the row of buttons in question */
static void
process_button_type_change (const char *name,
                            PurplePrefType type, gpointer value,
                            gpointer data)
{

/*
 * If the user changes the style of the buttons at the bottom of the
 * * conversation window, purple annoyingly removes all the buttons from
 * * the bbox, and reinserts its own.  So we need to reinsert our
 * * buttons as well. 
 */
  otrg_dialog_resensitize_all ();
}
#endif

static void
otr_options_cb (PurpleBlistNode * node, gpointer user_data)
{

/*
 * We've already checked PURPLE_BLIST_NODE_IS_BUDDY(node) 
 */
  PurpleBuddy *buddy = (PurpleBuddy *) node;

/*
 * Modify the settings for this buddy 
 */
  otrg_ui_config_buddy (buddy);
}

static void
supply_extended_menu (PurpleBlistNode * node, GList ** menu)
{

  PurpleMenuAction *act;

  PurpleBuddy *buddy;
  PurpleAccount *acct;
  const char *proto;

  if (!PURPLE_BLIST_NODE_IS_BUDDY (node))
    return;

/*
 * Extract the account, and then the protocol, for this buddy 
 */
  buddy = (PurpleBuddy *) node;
  acct = buddy->account;
  if (acct == NULL)
    return;
  proto = purple_account_get_protocol_id (acct);
  if (!otrg_plugin_proto_supports_otr (proto))
    return;
//[tag]make another function say CHAT room just support MSN
  act = purple_menu_action_new ("OTR Settings", (PurpleCallback)
                                otr_options_cb, NULL, NULL);
  *menu = g_list_append (*menu, act);
}

/* Disconnect a context, sending a notice to the other side, if
 * appropriate. */
void
otrg_plugin_disconnect (ConnContext * context)
{
  otrl_message_disconnect
    (otrg_plugin_userstate, &ui_ops, NULL,
     context->accountname, context->protocol, context->username);
}

/* Write the fingerprints to disk. */
void
otrg_plugin_write_fingerprints (void)
{
  gchar *storefile = g_build_filename (purple_user_dir (), STOREFNAME, NULL);

  otrl_privkey_write_fingerprints (otrg_plugin_userstate, storefile);
  g_free (storefile);
}


/* Find the ConnContext appropriate to a given PurpleConversation. */
ConnContext
  * otrg_plugin_chatuser_conv_to_context
  (PurpleConversation * conv, const char *username)
{
  PurpleAccount *account;
  const char *accountname, *proto;
  ConnContext *context;

  account = purple_conversation_get_account (conv);
  accountname = purple_account_get_username (account);
  proto = purple_account_get_protocol_id (account);

  context =
    otrl_context_find
    (otrg_plugin_userstate, username,
     accountname, proto, 0, NULL, NULL, NULL);

  return context;
}



/* Find the ConnContext appropriate to a given PurpleConversation. */
ConnContext *
otrg_plugin_conv_to_context (PurpleConversation * conv)
{
  PurpleAccount *account;
  char *username;
  const char *accountname, *proto;
  ConnContext *context;

  account = purple_conversation_get_account (conv);
  accountname = purple_account_get_username (account);
  proto = purple_account_get_protocol_id (account);

  username =
    g_strdup (purple_normalize
              (account, purple_conversation_get_name (conv)));
  context =
    otrl_context_find
    (otrg_plugin_userstate, username,
     accountname, proto, 0, NULL, NULL, NULL);
  g_free (username);
  return context;
}

/* Find the PurpleConversation appropriate to the given ConnContext.  If
 * one doesn't yet exist, create it if force_create is true. */
PurpleConversation
  * otrg_plugin_context_to_conv (ConnContext * context, int force_create)
{
  PurpleAccount *account;
  PurpleConversation *conv;

  account = purple_accounts_find (context->accountname, context->protocol);

  PurpleConnection *connection = purple_account_get_connection (account);

  if (account == NULL)
    return NULL;

  int chat_id = find_chat_id (context->accountname);

  purple_debug_info ("otrCHAT",
                     "[context_to_conv]%s, %d\n",
                     context->accountname, chat_id);

  if (chat_id != -1) {
    conv = purple_find_chat (connection, chat_id);

    if (!conv)
      return NULL;
  } else {


    conv = purple_find_conversation_with_account (PURPLE_CONV_TYPE_IM,
                                                  context->username, account);


    if (conv == NULL && force_create) {

      conv = purple_conversation_new
        (PURPLE_CONV_TYPE_IM, account, context->username);
    }
  }

  return conv;
}

/* What level of trust do we have in the privacy of this ConnContext? */
TrustLevel
otrg_plugin_context_to_trust (ConnContext * context)
{
  TrustLevel level = TRUST_NOT_PRIVATE;

  if (context && context->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
    if (context->active_fingerprint->trust
        && context->active_fingerprint->trust[0] != '\0') {
      level = TRUST_PRIVATE;
    } else {
      level = TRUST_UNVERIFIED;
    }
  } else if (context && context->msgstate == OTRL_MSGSTATE_FINISHED) {
    level = TRUST_FINISHED;
  }

  return level;
}

/* Send the OTRL_TLV_DISCONNECTED packets when we're about to quit. */
static void
process_quitting (void)
{
  ConnContext *context = otrg_plugin_userstate->context_root;

  while (context) {
    ConnContext *next = context->next;

    if (context->msgstate ==
        OTRL_MSGSTATE_ENCRYPTED && context->protocol_version > 1) {
      otrg_plugin_disconnect (context);
    }
    context = next;
  }



}

static gboolean
otr_plugin_load (PurplePlugin * handle)
{
  gchar *privkeyfile =
    g_build_filename (purple_user_dir (), PRIVKEYFNAME, NULL);
  gchar *storefile = g_build_filename (purple_user_dir (), STOREFNAME, NULL);
  void *conv_handle = purple_conversations_get_handle ();
  void *conn_handle = purple_connections_get_handle ();
  void *blist_handle = purple_blist_get_handle ();
  void *core_handle = purple_get_core ();

  if (!privkeyfile || !storefile) {
    g_free (privkeyfile);
    g_free (storefile);
    return 0;
  }

  otrg_plugin_handle = handle; //the handle for the plugin from PurplePlugin struct

/*
 * Make our OtrlUserState; we'll only use the one. 
 */
  otrg_plugin_userstate = otrl_userstate_create ();
  otrl_privkey_read (otrg_plugin_userstate, privkeyfile);
  g_free (privkeyfile);
  otrl_privkey_read_fingerprints
    (otrg_plugin_userstate, storefile, NULL, NULL);
  g_free (storefile);
  otrg_ui_update_fingerprint ();
  purple_signal_connect (core_handle, "quitting",
                         otrg_plugin_handle,
                         PURPLE_CALLBACK (process_quitting), NULL);
  purple_signal_connect (conv_handle,
                         "sending-im-msg",
                         otrg_plugin_handle,
                         PURPLE_CALLBACK (process_sending_im), NULL);
  purple_signal_connect (conv_handle,
                         "receiving-im-msg",
                         otrg_plugin_handle,
                         PURPLE_CALLBACK (process_receiving_im), NULL);
//[add]
  purple_signal_connect (conv_handle,
                         "sending-chat-msg",
                         otrg_plugin_handle,
                         PURPLE_CALLBACK (process_sending_chat), NULL);
  purple_signal_connect (conv_handle,
                         "receiving-chat-msg",
                         otrg_plugin_handle,
                         PURPLE_CALLBACK (process_receiving_chat), NULL);
  purple_signal_connect (conv_handle,
                         "chat-left",
                         otrg_plugin_handle,
                         PURPLE_CALLBACK (process_chat_left), NULL);
  purple_signal_connect (conv_handle,
                         "chat-buddy-joined",
                         otrg_plugin_handle,
                         PURPLE_CALLBACK (process_chat_buddy_joined), NULL);
//[end_add]
  purple_signal_connect (conv_handle,
                         "conversation-created",
                         otrg_plugin_handle,
                         PURPLE_CALLBACK (process_conv_create), NULL);
  purple_signal_connect (conn_handle, "signed-on",
                         otrg_plugin_handle,
                         PURPLE_CALLBACK (process_connection_change), NULL);
  purple_signal_connect (conn_handle,
                         "signed-off",
                         otrg_plugin_handle,
                         PURPLE_CALLBACK (process_connection_change), NULL);
  purple_signal_connect (blist_handle,
                         "blist-node-extended-menu",
                         otrg_plugin_handle,
                         PURPLE_CALLBACK (supply_extended_menu), NULL);
#if PURPLE_MAJOR_VERSION < 2
  button_type_cbid =
    purple_prefs_connect_callback
    ("/purple/gtk/conversations/button_type",
     process_button_type_change, NULL);
#endif
  purple_conversation_foreach (otrg_dialog_new_conv);
  return 1;
}

static gboolean
otr_plugin_unload (PurplePlugin * handle)
{
  void *conv_handle = purple_conversations_get_handle ();
  void *conn_handle = purple_connections_get_handle ();
  void *blist_handle = purple_blist_get_handle ();
  void *core_handle = purple_get_core ();

/*
 * Clean up all of our state. 
 */
  otrl_userstate_free (otrg_plugin_userstate);
  otrg_plugin_userstate = NULL;
  purple_signal_disconnect (core_handle,
                            "quitting",
                            otrg_plugin_handle,
                            PURPLE_CALLBACK (process_quitting));
  purple_signal_disconnect (conv_handle,
                            "sending-im-msg",
                            otrg_plugin_handle,
                            PURPLE_CALLBACK (process_sending_im));
  purple_signal_disconnect (conv_handle,
                            "receiving-im-msg",
                            otrg_plugin_handle,
                            PURPLE_CALLBACK (process_receiving_im));
//[add]
  purple_signal_disconnect (conv_handle,
                            "sending-chat-msg",
                            otrg_plugin_handle,
                            PURPLE_CALLBACK (process_sending_chat));
  purple_signal_disconnect (conv_handle,
                            "receiving-chat-msg",
                            otrg_plugin_handle,
                            PURPLE_CALLBACK (process_receiving_chat));
  purple_signal_disconnect (conv_handle,
                            "chat-left",
                            otrg_plugin_handle,
                            PURPLE_CALLBACK (process_chat_left));
  purple_signal_disconnect (conv_handle,
                            "chat-buddy-joined",
                            otrg_plugin_handle,
                            PURPLE_CALLBACK (process_chat_buddy_joined));
//[end_add]
  purple_signal_disconnect (conv_handle,
                            "conversation-created",
                            otrg_plugin_handle,
                            PURPLE_CALLBACK (process_conv_create));
  purple_signal_disconnect (conn_handle,
                            "signed-on",
                            otrg_plugin_handle,
                            PURPLE_CALLBACK (process_connection_change));
  purple_signal_disconnect (conn_handle,
                            "signed-off",
                            otrg_plugin_handle,
                            PURPLE_CALLBACK (process_connection_change));
  purple_signal_disconnect (blist_handle,
                            "blist-node-extended-menu",
                            otrg_plugin_handle,
                            PURPLE_CALLBACK (supply_extended_menu));
#if PURPLE_MAJOR_VERSION < 2
  purple_prefs_disconnect_callback (button_type_cbid);
#endif
  purple_conversation_foreach (otrg_dialog_remove_conv);
  return 1;
}

/* Return 1 if the given protocol supports OTR, 0 otherwise. */
int
otrg_plugin_proto_supports_otr (const char *proto)
{

/*
 * IRC is the only protocol we know of that OTR doesn't work on (its
 * * maximum message size is too small to fit a Key Exchange Message). 
 */
  if (proto && !strcmp (proto, "prpl-irc")) {
    return 0;
  }
  return 1;
}

//[add]

/* Return 1 if the given protocol CHAT ROOM supports OTR, 0 otherwise. */
int
otrg_plugin_chatroom_proto_supports_otr (const char *proto)
{

/*
 * MSN is the only protocol implemented in OTR chatroom now. 
 */
  if (proto && !strcmp (proto, "prpl-msn")) {
    return 1;
  }
  return 0;
}

//[end_add]

#ifdef USING_GTK

static PurplePluginUiInfo ui_info = {
  otrg_gtk_ui_make_widget
};

#define UI_INFO &ui_info
#define PLUGIN_TYPE PIDGIN_PLUGIN_TYPE
#else

#define UI_INFO NULL
#define PLUGIN_TYPE ""

#endif

static PurplePluginInfo info = {
  PURPLE_PLUGIN_MAGIC,
#if PURPLE_MAJOR_VERSION < 2

/*
 * We stick with the functions in the purple 1.0.x API for
 * * compatibility. 
 */
  1,               /* major version  */
  0,               /* minor version  */
#else

/*
 * Use the 2.0.x API 
 */
  2,               /* major version  */
  0,               /* minor version  */
#endif
  PURPLE_PLUGIN_STANDARD, /* type           */
  PLUGIN_TYPE,     /* ui_requirement */
  0,               /* flags          */
  NULL,            /* dependencies   */
  PURPLE_PRIORITY_DEFAULT, /* priority       */
  "otr",           /* id             */
  "Off-the-Record Messaging Extented Plugin MSN Multi-User Chat Support", /* name           */
  PIDGIN_OTR_VERSION, /* version        */

/*
 * summary        
 */
  "Provides private and secure conversations",

/*
 * description    
 */
  "Extention to original OTR plugin for purple, "
    "in order to support security Multi-User Chat Room in MSN",

/*
 * author         
 */
  "Jiang Bian and Dr. Seker <jxbian@ualr.edu>", "http://www.jiangbian.info/gotr", /* homepage       */
  otr_plugin_load, /* load           */
  otr_plugin_unload, /* unload         */
  NULL,            /* destroy        */
  UI_INFO,         /* ui_info        */
  NULL,            /* extra_info     */
  NULL,            /* prefs_info     */
  NULL             /* actions        */
};
static void
__init_plugin (PurplePlugin * plugin)
{

/*
 * Set up the UI ops 
 */
#ifdef USING_GTK
  otrg_ui_set_ui_ops (otrg_gtk_ui_get_ui_ops ());
  otrg_dialog_set_ui_ops (otrg_gtk_dialog_get_ui_ops ());
#endif

/*
 * Initialize the MD5_CRYPT library 
 */
  init_md5 (0);

//test_var = g_strdup_printf("%s", "test_var");

/*
 * Initialize the OTR library 
 */
  OTRL_INIT;
}

PURPLE_INIT_PLUGIN (otr, __init_plugin, info)
